package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/apache/rocketmq-client-go/v2/consumer"
	"github.com/apache/rocketmq-client-go/v2/primitive"
	"github.com/go-redsync/redsync/v4"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"mxshop_srvs/inventory_srv/global"
	"mxshop_srvs/inventory_srv/model"
	"mxshop_srvs/inventory_srv/proto"
	"sync"
)

type InventoryServer struct {
}

func (i *InventoryServer) SetInv(_ context.Context, req *proto.GoodsInvInfo) (*emptypb.Empty, error) {
	// 设置库存
	var inv model.Inventory
	global.DB.Where(&model.Inventory{Goods: req.GoodsId}).First(&inv, req.GoodsId)
	inv.Goods = req.GoodsId
	inv.Stocks = req.Num
	global.DB.Save(&inv)
	return &emptypb.Empty{}, nil
}
func (i *InventoryServer) InvDetail(_ context.Context, req *proto.GoodsInvInfo) (*proto.GoodsInvInfo, error) {
	// 获取库存详情
	var inv model.Inventory
	if result := global.DB.Where(&model.Inventory{Goods: req.GoodsId}).First(&inv, req.GoodsId); result.RowsAffected == 0 {
		return nil, status.Error(codes.NotFound, "库存信息不存在")
	}
	return &proto.GoodsInvInfo{
		GoodsId: req.GoodsId,
		Num:     inv.Stocks,
	}, nil
}

var lock sync.Mutex

func (i *InventoryServer) Sell(_ context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {
	var mutex *redsync.Mutex
	//lock.Lock()
	tx := global.DB.Begin() // 开启事务
	sellDetail := model.StockSellDetail{
		OrderSn: req.OrderSn,
		Status:  "1",
	}
	var details []model.GoodsDetail

	for _, goods := range req.GoodsInfo {
		details = append(details, model.GoodsDetail{
			Goods: goods.GoodsId,
			Num:   goods.Num,
		})
		goodsIdStr := fmt.Sprintf("goods:%d", goods.GoodsId)
		mutex = global.RedisLock.NewMutex(goodsIdStr)
		if err := mutex.Lock(); err != nil {
			return nil, status.Error(codes.Internal, "获取redis分布式锁失败")
		}
		zap.S().Info("获取redis分布式锁成功" + goodsIdStr)
		//for {
		var inv model.Inventory
		/*// 通过 FOR UPDATE 锁定库存信息，防止其他并发操作影响库存
		result := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where(&model.Inventory{Goods: goods.GoodsId}).First(&inv, goods.GoodsId)*/
		result := global.DB.Where(&model.Inventory{Goods: goods.GoodsId}).First(&inv, goods.GoodsId)
		if result.RowsAffected == 0 {
			tx.Rollback() // 回滚事务
			return nil, status.Error(codes.InvalidArgument, "库存信息不存在")
		}
		if inv.Stocks < goods.Num {
			tx.Rollback() // 回滚事务
			return nil, status.Error(codes.InvalidArgument, "库存不足")
		}
		inv.Stocks -= goods.Num // 扣减库存
		//global.DB.Save(&inv)
		tx.Save(&inv)
		//乐观锁
		//if result = tx.Model(&model.Inventory{}).Select("Stocks", "Version").Where("goods = ? and version = ?", inv.Goods, inv.Version).Updates(model.Inventory{Stocks: inv.Stocks, Version: inv.Version + 1}); result.RowsAffected == 0 {
		//	zap.S().Info("库存信息已被修改，请重试")
		//	continue
		//} else {
		//	zap.S().Info("扣减库存成功", goods.GoodsId, inv.Stocks)
		//	break
		//}
		//}

	}
	sellDetail.Detail = details
	if result := tx.Create(&sellDetail); result.RowsAffected == 0 {
		tx.Rollback() // 回滚事务
		return nil, status.Error(codes.Internal, "保存库存扣减历史失败")
	}
	tx.Commit() // 提交事务
	//lock.Unlock()
	if ok, err := mutex.Unlock(); !ok || err != nil { // 释放锁
		return nil, status.Error(codes.Internal, "释放redis分布式锁失败")
	}
	zap.S().Info("释放redis分布式锁成功")
	return &emptypb.Empty{}, nil
}
func (i *InventoryServer) Reback(_ context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {
	// 库存归还
	tx := global.DB.Begin()
	for _, goods := range req.GoodsInfo {
		var inv model.Inventory
		// 通过 FOR UPDATE 锁定库存信息，防止其他并发操作影响库存
		if result := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where(&model.Inventory{Goods: goods.GoodsId}).First(&inv, goods.GoodsId); result.RowsAffected == 0 {
			tx.Rollback() // 回滚事务
			return nil, status.Error(codes.InvalidArgument, "库存信息不存在")
		}
		// 增加库存
		inv.Stocks += goods.Num
		tx.Save(&inv)
	}
	tx.Commit() // 提交事务
	return &emptypb.Empty{}, nil
}

func AutoReback(ctx context.Context, msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
	type OrderInfo struct {
		OrderSn string
	}
	for i := range msgs {
		// 既然是归还库存，那么我应该知道具体的每件商品的数量 ，重复归还的原因，
		// 所以这个接口需要确保幂等性
		var orderInfo OrderInfo
		err := json.Unmarshal(msgs[i].Body, &orderInfo)
		if err != nil {
			zap.S().Errorf("解析json失败： %v \n", msgs[i].Body)
			return consumer.ConsumeSuccess, nil
		}
		// 将inv 的库存归还，将selldetail的状态改为2
		tx := global.DB.Begin()
		var sellDetail model.StockSellDetail
		if result := tx.Where(&model.StockSellDetail{OrderSn: orderInfo.OrderSn, Status: "1"}).First(&sellDetail); result.RowsAffected == 0 {
			return consumer.ConsumeSuccess, nil
		}
		// 如果查询到那么逐个商品归还库存
		for _, orderGood := range sellDetail.Detail {
			if result := tx.Model(&model.Inventory{}).Where(&model.Inventory{Goods: orderGood.Goods}).Update("stocks", gorm.Expr("stocks + ?", orderGood.Num)); result.RowsAffected == 0 {
				tx.Rollback() // 回滚事务
				return consumer.ConsumeRetryLater, nil
			}
		}
		sellDetail.Status = "2"
		if result := tx.Model(&model.StockSellDetail{}).Where(&model.StockSellDetail{OrderSn: orderInfo.OrderSn}).Update("status", "2"); result.RowsAffected == 0 {
			tx.Rollback() // 回滚事务
			return consumer.ConsumeRetryLater, nil
		}
		tx.Commit() // 提交事务
		return consumer.ConsumeSuccess, nil
	}
	return consumer.ConsumeSuccess, nil
}
